/*
 * SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

use anyhow::{Ok, Result};

use std::path::Path;

use crate::cli;
use crate::error::ZipCloakError;
use crate::utils;
use crate::zip;

pub fn main() -> Result<()> {
    // 初始化日志
    utils::common::init_logger();

    // 解析命令行参数
    let args = cli::parse_args_cloak();

    log::debug!("Parsed arguments: {:?}", args);

    // 打印相关的帮助信息
    {
        if args.version {
            cli::show_version_cloak();
            return Ok(());
        }
        if args.license {
            cli::show_license_cloak();
            return Ok(());
        }
    }

    // 获取要处理的ZIP文件路径
    if args.zipfile.is_none() {
        log::error!("Zip file not specified!");
        return Err(ZipCloakError::InvalidArguments("need to specify zip file".to_string()).into());
    }
    let zip_path = args.zipfile.clone().unwrap();
    // 检查ZIP文件是否存在
    if !zip_path.exists() {
        log::error!("Zip file not found: {}", zip_path.display());
        return Err(ZipCloakError::ArchiveNotFound(zip_path.display().to_string()).into());
    }

    let zip_archive = zip::ZipArchive::new(zip_path.to_str().unwrap())?;
    // 检查ZIP文件是否加密

    let header_counts = zip_archive.len();
    let encrypt_counts = zip_archive.encrypted_entries_count();
    log::debug!(
        "header_counts: {}, encrypt_counts: {}",
        header_counts,
        encrypt_counts
    );

    if args.decrypt {
        log::info!("Decrypting zip file: {}", zip_path.display());
        if encrypt_counts == 0 {
            log::warn!("Zip file is not encrypted!");
            return Err(ZipCloakError::NothingToDo("no encrypted files".to_string()).into());
        }
    } else {
        log::info!("Encrypting zip file: {}", zip_path.display());
        if encrypt_counts == header_counts {
            log::warn!("Zip files are already encrypted!");
            return Err(
                ZipCloakError::NothingToDo("all files encrypted already".to_string()).into(),
            );
        }
    }

    // 根据操作类型选择密码输入方式：解密时只需输入一次，加密时需要二次确认
    let passwd = if args.decrypt {
        utils::common::prompt_password()?
    } else {
        utils::common::prompt_and_verify_password()?
    };

    if passwd.is_empty() {
        log::error!("Password is empty!");
        return Err(ZipCloakError::InvalidArguments(
            "zero length password not allowed".to_string(),
        )
        .into());
    }
    log::debug!("password: {}", passwd);

    // 创建临时文件 - 优先使用-b指定的临时目录
    let temp_dir = if args.temp_path.is_some() {
        args.temp_path.clone().unwrap()
    } else {
        zip_path
            .parent()
            .unwrap_or_else(|| Path::new("."))
            .to_path_buf()
    };
    log::debug!("temp_dir: {}", temp_dir.display());

    // 确保临时目录存在
    if !temp_dir.exists() {
        std::fs::create_dir_all(&temp_dir)?;
    }

    // 生成临时文件名: ZI<6位十六进制进程ID>
    let pid = std::process::id();
    let temp_name = format!("ZI{:06X}", pid);
    let temp_path = temp_dir.join(temp_name);
    log::debug!("temp_path: {}", temp_path.display());

    std::fs::File::create(&temp_path)?;
    let mut zip_writer = zip::ZipWriter::new(temp_path.to_str().unwrap())?;

    for i in 0..header_counts {
        let entry = zip_archive.by_index_raw(i)?;
        let entry_name = entry.name().to_string();

        if args.decrypt {
            if entry.encrypted() {
                log::debug!("Decrypting entry: {}", entry_name);

                if entry.is_dir() {
                    if !args.quiet {
                        println!("decrypting: {}", entry_name);
                    }
                    let mut options = entry.options();
                    zip_writer.add_raw_file(&entry, &mut options, &[])?;
                    continue;
                }
                // 获取不解压的数据
                let datas_result = zip_archive.get_raw_file_data(&entry, Some(&passwd));
                if datas_result.is_err() {
                    if !args.quiet {
                        println!("decrypting: {} (wrong password--just copying)", entry_name);
                    }
                    zip_writer.raw_copy_file(&entry, entry_name)?;
                    continue;
                } else {
                    if !args.quiet {
                        println!("decrypting: {}", entry_name);
                    }
                    // 写入新的ZIP文件
                    let datas = datas_result.unwrap();
                    let mut options = entry.options();
                    zip_writer.add_raw_file(&entry, &mut options, datas.as_slice())?;
                }
            } else {
                log::debug!("Skipping unencrypted entry: {}", entry_name);
                if !args.quiet {
                    println!("   copying: {}", entry_name);
                }
                zip_writer.raw_copy_file(&entry, entry_name)?;
            }
        } else if entry.encrypted() {
            log::debug!("Skipping encrypted entry: {}", entry_name);
            if !args.quiet {
                println!("   copying: {}", entry_name);
            }
            zip_writer.raw_copy_file(&entry, entry_name)?;
        } else {
            log::debug!("Encrypting entry: {}", entry_name);
            if !args.quiet {
                println!("encrypting: {}", entry_name);
            }
            // 获取不解压的数据
            let datas = zip_archive.get_raw_file_data(&entry, None)?;
            // 写入新的ZIP文件
            let mut options = entry.options();
            options.with_password(&passwd);

            zip_writer.add_raw_file(&entry, &mut options, datas.as_slice())?;
        }
    }
    zip_writer.finish()?;

    // 替换原始ZIP文件
    // std::fs::rename(temp_file.path(), &zip_path)?;
    let save_path_file = if args.out.is_some() {
        args.out.clone().unwrap()
    } else {
        zip_path.clone()
    };

    // 如果目标文件已存在则先删除
    if save_path_file.exists() {
        std::fs::remove_file(&save_path_file)?;
    }

    // 重命名临时文件到目标路径
    crate::utils::common::safe_move_file(&temp_path, &save_path_file)?;

    log::info!(
        "{} zip file: {}",
        if args.decrypt {
            "Decrypted"
        } else {
            "Encrypted"
        },
        save_path_file.display()
    );
    Ok(())
}
